﻿
namespace Patterns4Net.Core.AbstractionLevel
{
    using System.Collections.Generic;
    using System.Diagnostics.Contracts;
    using System.Xml.Linq;
    using System.Xml.XPath;
    using Patterns4Net.Attributes;

    public class ConfigPatternsLevelProvider : IPatternLevelProvider
    {
        private readonly IDictionary<string, AbstractionLevel?> mapping;

        private ConfigPatternsLevelProvider(IDictionary<string, AbstractionLevel?> mapping)
        {
            this.mapping = mapping;
        }

        public static ConfigPatternsLevelProvider Create(XDocument config)
        {
            var mapping = new Dictionary<string, AbstractionLevel?>();
            foreach (var element in config.XPathSelectElements("//pattern"))
            {
                var name = element.XPathSelectElement("class").Attribute("full-name").Value;
                var levelElement = element.XPathSelectElement("level");
                if (levelElement == null)
                {
                    mapping.Add(name, null);
                }
                else
                {
                    var value = element.XPathSelectElement("level").Value;
                    mapping.Add(name, GetLevel(value));
                }
            }

            return new ConfigPatternsLevelProvider(mapping);
        }

        public AbstractionLevel? GetLevel(IPattern pattern)
        {
            AbstractionLevel? result;
            if (this.mapping.TryGetValue(pattern.GetType().FullName, out result))
            {
                return result;
            }
            else
            {
                return null;
            }
        }

        private static AbstractionLevel GetLevel(string str)
        {
            switch (str)
            {
                case "higher":
                    return AbstractionLevel.Higher;
                case "low":
                    return AbstractionLevel.Low;
                case "normal":
                    return AbstractionLevel.Normal;
                default:
                    Contract.Assert(
                        false, "Switch in ConfigPatternsLevelProvider.GetLevel(string): unexpected value of parameter");
                    return AbstractionLevel.Normal;
            }            
        }
    }
}
